package module.modbus.rtu;

import common.BasicSerialPort;
import common.Const;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.rmi.server.ActivatableServerRef;
import util.ByteUtil;
import util.CRC;
import util.HexFormat;
import java.io.IOException;
import java.security.spec.ECField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.TooManyListenersException;

/**
 * @author bingo
 * @Description   不使用Modbus4j进行发送
 * @Date 2018/8/10
 *
 *   功能码:
 *        3  读多个功能码
 *        6  写一个寄存器功能码
 *        16 写多个寄存器功能码
 *
 *
 */
public class ModbusRtu extends BasicSerialPort{
    private Logger logger = LoggerFactory.getLogger(ModbusRtu.class);

    private int sendCount = 0;

    private int successCount=0;

    private int failCount = 0;

    private int crcFailureCount  = 0;

    public int getSendCount() {
        return sendCount;
    }


    private String temparature;

    public String getTemparature() {
        return temparature;
    }

    public int getSuccessCount() {
        return successCount;
    }

    /**
  *  打开串口
  * */
    public SerialPort open(CommPortIdentifier commPortIdentifier)  {

        try {
           serialPort  = (SerialPort) commPortIdentifier.open(Object.class.getSimpleName(),2000);
           serialPort.notifyOnDataAvailable(true);
           serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
           serialPort.addEventListener(new SerialPortListener());
            //设置当有数据到达时唤醒监听接收线程
            serialPort.notifyOnDataAvailable(true);
            //设置当通信中断时唤醒中断线程
            serialPort.notifyOnBreakInterrupt(true);
        } catch (PortInUseException e) {
            e.printStackTrace();
        } catch (TooManyListenersException e) {
            e.printStackTrace();
        } catch (UnsupportedCommOperationException e) {
            e.printStackTrace();
        }
        return serialPort;
    }

    /***
     *   基于串口协议数据的发送
     *   @param value  具体发送的数据
     *   @param slaveId 主机号
     *   @param  methodCode 方法码
     *   @param  startOffset 起始地址
     * */
    public void send(int slaveId, int methodCode, int startOffset, short[] value){
        String str  ="";
        if(methodCode== Const.READ&&value.length==1){
            str = read(slaveId,methodCode,startOffset,value[0]);

        }else if(methodCode==Const.SINGLE_WRITE&&value.length==1){
            str = write(slaveId,methodCode,startOffset,value[0]);

        }else if(methodCode==Const.MULTI_WRITE&&value.length>=1){
            str =   write(slaveId,methodCode,startOffset,value);

        }else {
            logger.info("数据未发送~~,功能码没找到,请查看数据格式是否正确");
        }
        byte[] bys = ByteUtil.hexStr2Byte(str.toString());
        String crcCode = CRC.getCRC(bys);
        String sendData  = str+crcCode;
            logger.info("十六进制发送数据：{}",sendData);
        bys = ByteUtil.hexStr2Byte(sendData);
        logger.info("字节数组信息：{}",Arrays.toString(bys));
        if(serialPort!=null){
            try {
                sendCount++;
                outputStream = serialPort.getOutputStream();
                outputStream.write(bys);
            } catch (IOException e) {
                e.printStackTrace();
            }

       }
    }
    /**
     *  此方法对应16号功能码，写多个数据
     * */
       private String  write(int slaveId, int methodCode, int startOffset, short value[]){
        StringBuffer stringBuffer = new StringBuffer();
        String hexSlaveId = Integer.toHexString(slaveId);
        stringBuffer.append(HexFormat.formatSimpple(hexSlaveId));
        String hexMethodCode = Integer.toHexString(methodCode);
        stringBuffer.append(HexFormat.formatSimpple(hexMethodCode));
        String hexStart = HexFormat.formatHex(Integer.toHexString(startOffset));
        stringBuffer.append(hexStart);
        String hexCount = Integer.toHexString(value.length);
        stringBuffer.append(HexFormat.formatHex(hexCount));
        String hexByteLength = Integer.toHexString(value.length*2);
        stringBuffer.append(HexFormat.formatSimpple(hexByteLength));
        for(short item:value){
            String hexValue = HexFormat.formatHex(Integer.toHexString(item));
            stringBuffer.append(hexValue);
        }
         return  stringBuffer.toString();
    }

    /**
     *  此方法对应6号功能码，写单个数据
     * */
    private String  write(int slaveId, int methodCode, int startOffset, short value){

        return  splicingData(slaveId,methodCode,startOffset,value);

    }

    /**
     *  此方法对应3号功能码，读数据
     *
     * */
    private String  read(int slaveId, int methodCode, int startOffset, short value){
        return  splicingData(slaveId,methodCode,startOffset,value);
    }


    /**
     *   拼接读以及单个写数据
     *
     * */

    private  String splicingData(int slaveId, int methodCode, int startOffset, short value){
        StringBuffer stringBuffer = new StringBuffer();

        String hexSlaveId = Integer.toHexString(slaveId);
        stringBuffer.append(HexFormat.formatSimpple(hexSlaveId));

        String hexMethodCode = Integer.toHexString(methodCode);
        stringBuffer.append(HexFormat.formatSimpple(hexMethodCode));

        String hexStart = HexFormat.formatHex(Integer.toHexString(startOffset));
        stringBuffer.append(hexStart);

        String hexValue = HexFormat.formatHex(Integer.toHexString(value));
        stringBuffer.append(hexValue);
        return  stringBuffer.toString();
    }
   /**
    *    数据接受方法
    *
    * */
   @Override
    public String receive(){
       try {
           Thread.sleep(50);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       try {
          inputStream = serialPort.getInputStream();
           int len  = inputStream.available();
            byte[] bys = null;
            while (len!=0){
                bys = new byte[len];
                inputStream.read(bys);
                len = inputStream.available();
            }
       /*  int len = 0;
          byte[] bys = new byte[10];
          while ((len = inputStream.read(bys))!=-1){
              logger.info("GO!!!!!!!!!!!!1111");
              logger.info(ByteUtil.byteArrayToHexString(bys));
          }*/
            String msg = ByteUtil.byteArrayToHexString(bys);
               logger.info("十六进制返回数据:{}",msg);
            String receiveCrc = msg.substring(msg.length()-4);
            String data  = msg.substring(0,msg.length()-4);
            String checkRes = CRC.getCRC(ByteUtil.hexStr2Byte(data));
               if (checkRes.equalsIgnoreCase(receiveCrc)){
                logger.info("校验CRC：{},接受CRC:{},检验成功！",checkRes,receiveCrc);
                int methodCode = Integer.parseInt(data.substring(3,4),16);
                successCount++;
                //如果请求码大于128说明有错误
                if(methodCode>=128){
                    //  todo  读取错误信息
                    //如果是读请求
                }else if(methodCode==Const.READ){
                    List list = new ArrayList();

                    //获取数据长度
                    int dataLength = Integer.parseInt(data.substring(4,6),16);
                    logger.info("返回数据长度:{}",dataLength);
                    short[] res = new short[dataLength];
                    for(int i=0;i<dataLength;i++){
                       int startIndex = 2*i+6;
                       res[i] = (short) Integer.parseInt(data.substring(startIndex,startIndex+2),16);
                      //logger.info("item{}",res[i]);
                      list.add(i,data.substring(startIndex,startIndex+2));
                    }
                   // getTemperature(list);
                    //如果是写请求
                }else if(methodCode==Const.SINGLE_WRITE || methodCode ==Const.MULTI_WRITE){

                           //todo
                }


            }
            else{
                   crcFailureCount++;
                   logger.info("校验CRC：{},接受CRC:{}",checkRes,receiveCrc);
                   logger.error("CRC校验失败");
               }
        } catch (IOException e) {
           failCount++;
            e.printStackTrace();
        }
       System.out.println("已经发送：["+getSendCount()+"]成功:["+getSuccessCount()+"]CRC校验失败:"+crcFailureCount);
       RtuStartThread.isLocked = false;
        return  null;

    }

    public void  closePort(){
       if(serialPort!=null){ serialPort.notifyOnDataAvailable(false);
          serialPort.removeEventListener();
          serialPort.close();
          logger.info("{}串口关闭成功~~·",serialPort.getName());
       }
    }

    /**
     *   得到的数据为 80 00 42 01 ，数据的排列顺序为 2-1-4-3
     *   数据从高到低的排列为：42 01 80 00 ，该数据类型为IEEE754单精度浮点数，
     *
     * */
    public void getTemperature(List<String> list){
        System.out.println("数据长度"+list.size());
        try {
            StringBuffer stringBuffer = new StringBuffer();
       stringBuffer.append(list.get(2));
       stringBuffer.append(list.get(3));
       stringBuffer.append(list.get(0));
       stringBuffer.append(list.get(1));
       logger.info("hexStr{}",stringBuffer.toString());
       int  hex = Integer.parseInt(stringBuffer.toString(),16);
        System.out.println("温度:"+Float.intBitsToFloat(hex)+"c");
            temparature = "温度:"+Float.intBitsToFloat(hex)+"摄氏度";
        }catch (Exception e){
            logger.info("数据转换异常！");
            e.printStackTrace();
        }

    }


}
